/*
 * Copyright (c) 2023, mr-library Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2023-04-23     billy       first version
 */

#include "lcd.h"

#if (MR_CFG_LCD == MR_CFG_ENABLE)

static mr_err_t err_io_lcd_configure(mr_lcd_t lcd, struct mr_lcd_config *config)
{
    return -MR_ERR_IO;
}

static void err_io_lcd_write(mr_lcd_t lcd, void *disp_drv, void *area, void *color_p)
{

}

static mr_uint8_t err_io_lcd_read(mr_lcd_t lcd)
{
    return 0;
}

static mr_err_t mr_lcd_open(mr_device_t device)
{
    mr_lcd_t lcd = (mr_lcd_t)device;
    struct mr_lcd_config default_config = MR_LCD_CONFIG_DEFAULT;

    /* Enable lcd using the default config */
    lcd->config = default_config;

    return lcd->ops->configure(lcd, &lcd->config);
}

static mr_err_t mr_lcd_close(mr_device_t device)
{
    mr_lcd_t lcd = (mr_lcd_t)device;

    /* Disable lcd */
    return lcd->ops->configure(lcd, &lcd->config);
}

static mr_err_t mr_lcd_ioctl(mr_device_t device, int cmd, void *args)
{
    mr_lcd_t lcd = (mr_lcd_t)device;
    mr_err_t ret = MR_ERR_OK;

    switch (cmd)
    {
        case MR_DEVICE_CTRL_SET_CONFIG:
        {
            if (args)
            {
                mr_lcd_config_t config = (mr_lcd_config_t)args;
                memcpy(&config, args, sizeof(mr_lcd_config_t));
                ret = lcd->ops->configure(lcd, config);
                if (ret == MR_ERR_OK)
                {
                    memcpy(&lcd->config, &config, sizeof(mr_lcd_config_t));
                }
                return ret;
            }
            return -MR_ERR_INVALID;
        }

        case MR_DEVICE_CTRL_GET_CONFIG:
        {
            if (args)
            {
                memcpy(args, &lcd->config, sizeof(lcd->config));
                return MR_ERR_OK;
            }
            return -MR_ERR_INVALID;
        }

        case MR_DEVICE_CTRL_SET_POWER:
        {
            lcd->config.power = *((mr_uint32_t *)args);
        }

        case MR_DEVICE_CTRL_GET_POWER:
        {
            *((mr_uint32_t *)args) = lcd->config.power;
            return MR_ERR_OK;
        }

        case MR_DEVICE_CTRL_SET_BACKLIGHT:
        {
            lcd->config.backlight = *((mr_uint32_t *)args);
            return MR_ERR_OK;
        }

        case MR_DEVICE_CTRL_GET_BACKLIGHT:
        {
            *((mr_uint32_t *)args) = lcd->config.backlight;
            return MR_ERR_OK;
        }

        default:
            return -MR_ERR_UNSUPPORTED;
    }
}

static mr_ssize_t mr_lcd_read(mr_device_t device, mr_off_t pos, void *buffer, mr_size_t size)
{
    return (mr_ssize_t)0;
}

static mr_ssize_t mr_lcd_write(mr_device_t device, mr_off_t off, const void *buffer, mr_size_t size)
{
    mr_lcd_t lcd = (mr_lcd_t)device;
    mr_uint32_t *buf = (void *)buffer;
    lcd->ops->write(lcd, (void *)buf, (void *)buf++, (void *)buf++);
    return (mr_ssize_t)0;
}

/**
 * @brief This function adds the lcd device.
 *
 * @param lcd The lcd device to be added.
 * @param name The name of the device.
 * @param ops The operations of the device.
 * @param data The private data of the device.
 *
 * @return MR_ERR_OK on success, otherwise an error code.
 */
mr_err_t mr_lcd_device_add(mr_lcd_t lcd, const char *name, struct mr_lcd_ops *ops, void *data)
{
    static struct mr_device_ops device_ops =
        {
            mr_lcd_open,
            mr_lcd_close,
            mr_lcd_ioctl,
            mr_lcd_read,
            mr_lcd_write,
        };
    mr_uint16_t support_flag = MR_DEVICE_OFLAG_RDWR;

    MR_ASSERT(lcd != MR_NULL);
    MR_ASSERT(name != MR_NULL);
    MR_ASSERT(ops != MR_NULL);

    /* Non-blocking mode */
    support_flag |= MR_DEVICE_OFLAG_NONBLOCKING;

    /* Protect every operation of the lcd device */
    ops->configure = ops->configure ? ops->configure : err_io_lcd_configure;
    ops->write = ops->write ? ops->write : err_io_lcd_write;
    ops->read = ops->read ? ops->read : err_io_lcd_read;
    lcd->ops = ops;

    /* Add the device */
    return mr_device_add(&lcd->device, name, Mr_Device_Type_Lcd, support_flag, &device_ops, data);
}

#endif